﻿#include "stdafx.h"
#include "global.h"
#include "d3dfunc.h"
#include "convert.h"
#include "resource.h"
#include "addnoise.h"
#include "filterimage.h"
#include "controls.h"
#include "Interface.h"

/********************************************************************
* DDrawInit initializes Direct Draw and create main surfaces        *
* hWnd - the window this function is loaded from		(in)        *
********************************************************************/

HRESULT D3DInit(HWND hWnd)
{
	GV.SourceImg = 0;
	GV.NoiseImg = 0;
	GV.FilteredImg = 0;
	GV.DiffImg = 0;
	RECT wndRect;
	RECT clientRect;
	GetWindowRect(hWnd, &wndRect);
	GetClientRect(hWnd, &clientRect);
	int wndWidth = CLIENT_WIDTH + (wndRect.right-wndRect.left) -
		(clientRect.right - clientRect.left);
	int wndHeight = CLIENT_HEIGHT + (wndRect.bottom-wndRect.top) -
		(clientRect.bottom-clientRect.top);
	MoveWindow(hWnd, 50, 50,
		wndWidth,wndHeight,TRUE);
	if (NULL == (GV.pD3D = Direct3DCreate9(D3D_SDK_VERSION)))
		return E_FAIL;
	D3DPRESENT_PARAMETERS d3dpp;
	ZeroMemory(&d3dpp, sizeof(d3dpp));
	d3dpp.Windowed = TRUE;
	d3dpp.SwapEffect = D3DSWAPEFFECT_DISCARD;
	d3dpp.BackBufferFormat = D3DFMT_UNKNOWN;

	if (FAILED(GV.pD3D->CreateDevice(D3DADAPTER_DEFAULT, D3DDEVTYPE_HAL, hWnd,
		D3DCREATE_MIXED_VERTEXPROCESSING, &d3dpp, &GV.pD3Device)))
		return E_FAIL;
	GV.LoadingPhase = 0;
	GV.FilterMatrix[12] = 1.f;

	return S_OK;
}

HRESULT GetPictureSizeString(DWORD Width, DWORD Height, LPWSTR Output)
{
	DWORD n = 1, m;
	DWORD Cap = 10;
	while (Width >= Cap)
	{
		Cap = Cap * 10;
		n++;
	}
	for (DWORD i = 0; i < n; i++)
	{
		Width = Width % Cap;
		Cap = Cap / 10;
		Output[i] = Width / Cap + L'0';
	}
	Cap = 10;
	Output[n] = 'x';
	m = n + 2;
	while (Height >= Cap)
	{
		Cap = Cap * 10;
		m++;
	}
	for (DWORD i = n + 1; i < m; i++)
	{
		Height = Height % Cap;
		Cap = Cap / 10;
		Output[i] = Height / Cap + L'0';
	}
	Output[m] = 0;
	return S_OK;
}

HRESULT GetValueString(LPWSTR String, DWORD Start, DWORD Main, LONG Exp, DWORD* Len)
{
	DWORD n = 1, Cap = 10;
	while (Main >= Cap)
	{
		Cap = Cap * 10;
		n++;
	}
	n = n + Start;
	for (DWORD i = Start; i < n; i++)
	{
		Main = Main % Cap;
		Cap /= 10;
		String[i] = Main / Cap + L'0';
	}
	if (Exp == 0)
	{
		String[n] = 0;
		if (Len) 
			*Len = n - Start;
		return S_OK;
	}
	String[n] = L'e'; n++;
	if (Exp < 0)
	{
		String[n] = L'-'; 
		n++;
		Exp = -Exp;
	}
	DWORD Start2 = n; n = 1;  Cap = 10;
	while (Exp >= Cap)
	{
		Cap = Cap * 10;
		n++;
	}
	n = n + Start2;
	for (DWORD i = Start2; i < n; i++)
	{
		Exp = Exp % Cap;
		Cap /= 10;
		String[i] = Exp / Cap + L'0';
	}

	String[n] = 0;
	if (Len)
		*Len = n - Start;
	return S_OK;
}

HRESULT GetValueString(LPWSTR String, DWORD Start, double Value, DWORD Presicion, DWORD *Len)
{
	DWORD n = 0, Cap = 1;
	DWORD Start2 = Start;
	if (Value < 0)
	{
		String[Start2] = L'-';
		Value = - Value;
		Start2++;
	}
	if (Value < 1 )
	{
		String[Start2] = L'0';
		Start2++;
	}
	while (Value >= Cap)
	{
		Cap = Cap * 10;
		n++;
	}
	n = n + Start2;
	Value = Value / Cap;
	for (DWORD i = Start2; i < n; i++)
	{
		Value *= 10.f;
		DWORD CurDigit = (DWORD) Value;
		String[i] = CurDigit + L'0';
		Value -= CurDigit;
	}
	if (Presicion == 0)
	{
		String[n] = 0;
		if (Len)
			*Len = n - Start;
		return S_OK;
	}
	String[n] = '.';
	n++;
	for (DWORD i = n; i < n + Presicion; i++)
	{
		Value *= 10.f;
		DWORD CurDigit = (DWORD) Value;
		String[i] = CurDigit + L'0';
		Value -= CurDigit;
	}
	String[n + Presicion] = 0;
	if (Len)
		*Len = n + Presicion - Start;
	return S_OK;
}


HRESULT GetWindowString(DWORD Size, LPWSTR Output)
{
	Size = 2 * Size + 1;
	DWORD n = 1, Cap = 10;
	while (Size >= Cap)
	{
		Cap = Cap * 10;
		n++;
	}
	Output[n] = L'x';
	for (DWORD i = 0; i < n; i++)
	{
		Size = Size % Cap;
		Cap /= 10;
		Output[i] = Size / Cap + L'0';
		Output[i + n + 1] = Output[i];
	}
	Output[2 * n + 1] = 0;
	return 1;
}

/********************************************************************
* SetFileParams sets directory, file name and offsets to GV
********************************************************************/

HRESULT SetFileParams(LPCWSTR FullPath)
{
	LPCSTR Var[4] = {".jpg", ".png",".JPG", ".PNG"};
	DWORD i = 0, Ch = 0;
	while (FullPath[i] != 0)
		i++;
	if (i < 5)
		return E_FAIL;
	if (i > 1023)
		return E_FAIL;
	for (DWORD j = 0; j < 4; j++)
	{
		DWORD TempCh = 0;
		for (DWORD k = 0; k < 4; k++)
		{
			if (FullPath[i - 4 + k] != Var[j][k])
				TempCh += 1;
		}
		if (TempCh)
			Ch++;
	}
	if (Ch != 3)
		return E_FAIL;
	memcpy(GV.FileName, FullPath, (i + 1) * sizeof(WCHAR));
	GV.FileExtOffset = i - 3;
	while (FullPath[i] != '\\')
		i--;
	GV.FileNameOffset = i + 1;
	return S_OK;
}

/********************************************************************
* ClearDDraw releases ddraw files (at least it should do it)        *
********************************************************************/

HRESULT ClearD3D()
{
	if (GV.ImageMatrix)		{ delete[] GV.ImageMatrix;		GV.ImageMatrix = 0;	}
	if (GV.NoiseMatrix)		{ delete[] GV.NoiseMatrix;		GV.NoiseMatrix = 0;	}
	if (GV.FiltrMatrix)		{ delete[] GV.FiltrMatrix;		GV.FiltrMatrix = 0;	}
	if (GV.DiffrMatrix)		{ delete[] GV.DiffrMatrix;		GV.DiffrMatrix = 0;	}
	if (GV.RGBMatrix)		{ delete[] GV.RGBMatrix;		GV.RGBMatrix = 0;	}
	if (GV.SourceImg)		{ GV.SourceImg->Release();		GV.SourceImg = 0;	}
	if (GV.NoiseImg)		{ GV.NoiseImg->Release();		GV.NoiseImg = 0;	}
	if (GV.FilteredImg)		{ GV.FilteredImg->Release();	GV.FilteredImg = 0;	}
	if (GV.DiffImg)			{ GV.DiffImg->Release();		GV.DiffImg = 0;		}
	if (GV.MyControls)		{ GV.MyControls->Release();		GV.MyControls = 0;	}
	if (GV.Elements)		{ GV.Elements->Release();		GV.Elements = 0;	}
	if (GV.Elements2)		{ GV.Elements2->Release();		GV.Elements2 = 0;	}
	if (GV.ImgBack)			{ GV.ImgBack->Release();		GV.ImgBack = 0;		}
	if (GV.ImgTrans)		{ GV.ImgTrans->Release();		GV.ImgTrans = 0;	}
	if (GV.pD3Device)		{ GV.pD3Device->Release();		GV.pD3Device = 0;	}
	if (GV.pD3D)			{ GV.pD3D->Release();			GV.pD3D = 0;		}
	return S_OK;
}

/********************************************************************
* Render draws everything on the screen
********************************************************************/

HRESULT Render(HWND hWnd)
{
	GV.pD3Device->Clear(0,NULL, D3DCLEAR_TARGET, 0xd6e3ed,
		1.0f,0);
	if (GV.LoadingPhase > 100)
	{
		GV.LockInterface = 1;
		GV.LoadingPhase -= 100;
	}
	else if (GV.LoadingPhase == 1)
	{
		GV.LoadingPhase = 0;
		SetCursor(LoadCursor(0, IDC_WAIT));
		// Загрузка тестового изображения
		D3DXIMAGE_INFO FileInfo;
		HRESULT r = D3DXGetImageInfoFromFile(GV.FileName, &FileInfo);
		if (SUCCEEDED(r))
		{
			PictureText *Properties = (PictureText *) GetControlByID(26);
			Properties->SetText(GV.FileName + GV.FileNameOffset, 0);
			WCHAR Size[256];
			GetPictureSizeString(FileInfo.Width, FileInfo.Height, Size);
			Properties->SetText(L", ", GV.FileExtOffset - GV.FileNameOffset + 3);
			Properties->SetText(Size, GV.FileExtOffset - GV.FileNameOffset + 5);
			if (GV.ImageMatrix)		{ delete[] GV.ImageMatrix;		GV.ImageMatrix = 0;	}
			if (GV.NoiseMatrix)		{ delete[] GV.NoiseMatrix;		GV.NoiseMatrix = 0;	}
			if (GV.FiltrMatrix)		{ delete[] GV.FiltrMatrix;		GV.FiltrMatrix = 0;	}
			if (GV.DiffrMatrix)		{ delete[] GV.DiffrMatrix;		GV.DiffrMatrix = 0;	}
			if (GV.RGBMatrix)		{ delete[] GV.RGBMatrix;		GV.RGBMatrix = 0;	}
			if (GV.SourceImg)		{ GV.SourceImg->Release();		GV.SourceImg = 0;	}
			if (GV.NoiseImg)		{ GV.NoiseImg->Release();		GV.NoiseImg = 0;	}
			if (GV.FilteredImg)		{ GV.FilteredImg->Release();	GV.FilteredImg = 0;	}
			if (GV.DiffImg)			{ GV.DiffImg->Release();		GV.DiffImg = 0;		}

			((PictureImage *) GetControlByID(20))->source = 0;
			((PictureImage *) GetControlByID(21))->source = 0;

			GV.ImageSize.cx = FileInfo.Width;
			GV.ImageSize.cy = FileInfo.Height;
			GV.pD3Device->CreateOffscreenPlainSurface(FileInfo.Width, FileInfo.Height, D3DFMT_A8R8G8B8,
				D3DPOOL_DEFAULT, &GV.SourceImg, 0);
			GV.pD3Device->CreateOffscreenPlainSurface(FileInfo.Width, FileInfo.Height, D3DFMT_A8R8G8B8,
				D3DPOOL_DEFAULT, &GV.NoiseImg, 0);
			GV.pD3Device->CreateOffscreenPlainSurface(FileInfo.Width, FileInfo.Height, D3DFMT_A8R8G8B8,
				D3DPOOL_DEFAULT, &GV.FilteredImg, 0);
			GV.pD3Device->CreateOffscreenPlainSurface(FileInfo.Width, FileInfo.Height, D3DFMT_A8R8G8B8,
				D3DPOOL_DEFAULT, &GV.DiffImg, 0);
				D3DXLoadSurfaceFromFile(GV.SourceImg,0,0, GV.FileName, 0, D3DX_FILTER_NONE, 0, 0);
			// Get pixels of image
			DWORD ImageNPixels = FileInfo.Width * FileInfo.Height;
			GV.ImageMatrix = new Color[ImageNPixels];
			GV.NoiseMatrix = new Color[ImageNPixels];
			GV.FiltrMatrix = new Color[ImageNPixels];
			GV.DiffrMatrix = new Color[ImageNPixels];

			GetColors(GV.SourceImg,(DWORD *) GV.ImageMatrix);

			((PictureDraggableImage *) GetControlByID(12))->InsertSurface(GV.SourceImg);
			GV.LoadingPhase = 2;
		}
		SetCursor(LoadCursor(0, IDC_ARROW));
		// Если загрузка не успешна, интерфейс разблокируется
		if (GV.LoadingPhase == 0)
			GV.LockInterface = timeGetTime();
	}
	else if (GV.LoadingPhase == 2)
	{
		SetCursor(LoadCursor(0, IDC_WAIT));
		if (GV.ImageMatrix)
		{
			memcpy(GV.NoiseMatrix,GV.ImageMatrix, GV.ImageSize.cx * GV.ImageSize.cy * sizeof(Color));
			// Добавление шума
			PictureList *NoiseList = (PictureList *) GetControlByID(0);
			VSlider *NoiseStrenght = (VSlider *) GetControlByID(1);
			WCHAR Value[16];
			PictureList *Type = (PictureList *) GetControlByID(0);
			PictureText *Text = (PictureText *) GetControlByID(3);
			if (Type->SelectedItem == 1)
			{
				DWORD Sigma = (6 - NoiseStrenght->Position) * 3;
				WCHAR Value[16];
				Value[0] = L'σ';
				Value[1] = L'=';
				GetValueString(Value, 2, Sigma, -4, 0);
				Text->SetText(Value, 0);
				SetGaussNoise(GV.NoiseMatrix, GV.ImageSize.cx, GV.ImageSize.cy, Sigma);
			}
			else if (Type->SelectedItem == 2)
			{
				DWORD Persent = (6 - NoiseStrenght->Position) * 2;
				DWORD Len;
				WCHAR Value[16];
				Value[0] = 'p'; Value[1] = '=';
				GetValueString(Value, 2, Persent, 0, &Len);
				Value[2 + Len] = '%';
				Value[3 + Len] = 0;
				Text->SetText(Value, 0);
				SetPulseNoise(GV.NoiseMatrix, GV.ImageSize.cx, GV.ImageSize.cy, Persent);
			}
			SetColors(GV.NoiseImg, (DWORD *) GV.NoiseMatrix);
			((PictureDraggableImage *) GetControlByID(13))->InsertSurface(GV.NoiseImg);
		}
		GV.LoadingPhase = 3;
		SetCursor(LoadCursor(0, IDC_ARROW));
	}
	else if (GV.LoadingPhase == 3)
	{
		SetCursor(LoadCursor(0, IDC_WAIT));
		if (GV.ImageMatrix)
		{
			// Фильтрация
			PictureList *FilterList = (PictureList *) GetControlByID(4);
			VSlider *FilterStrenght = (VSlider *) GetControlByID(5);
			if (FilterList->SelectedItem == 0)
			{
				GaussFilter(GV.NoiseMatrix, GV.FiltrMatrix, GV.ImageSize.cx, GV.ImageSize.cy, 0, 1);
			}
			else if (FilterList->SelectedItem == 1)
			{
				DWORD Step = (6 - FilterStrenght->Position) * 10;
				WCHAR Value[16];
				DWORD *Len = new DWORD; 
				Value[0] = L'M'; Value[1] = L'i'; Value[2] = L'n'; Value[3] = L'=';
				GetValueString(Value, 4, Step * 100 / 255, 0, Len);
				Value[4 + *Len] = '%'; Value[5 + *Len] = 0;
				((PictureText *)GetControlByID(7))->SetText(Value, 0);
				delete Len;
				StepFilter(GV.NoiseMatrix, GV.FiltrMatrix, GV.ImageSize.cx, GV.ImageSize.cy, Step);
			}
			else if (FilterList->SelectedItem == 2)
			{
				DWORD Size = (3 - FilterStrenght->Position);
				WCHAR Value[16];
				GetWindowString(Size, Value);
				((PictureText *)GetControlByID(7))->SetText(Value, 0);
				MedianFilter(GV.NoiseMatrix, GV.FiltrMatrix, GV.ImageSize.cx, GV.ImageSize.cy, (3 - FilterStrenght->Position));
			}
				
			SetColors(GV.FilteredImg, (DWORD *) GV.FiltrMatrix);
			((PictureDraggableImage *) GetControlByID(14))->InsertSurface(GV.FilteredImg);
		}
		GV.LoadingPhase = 4;
		SetCursor(LoadCursor(0, IDC_ARROW));

	}
	else if (GV.LoadingPhase == 4)
	{
		SetCursor(LoadCursor(0, IDC_WAIT));
		if (GV.ImageMatrix)
		{
			LPCOLOR Diff1, Diff2;
			PictureList *DiffLst1 = (PictureList *)GetControlByID(23);
			PictureList *DiffLst2 = (PictureList *)GetControlByID(25);
			Diff1 = GV.ImageMatrix;
			Diff2 = GV.FiltrMatrix;
			if (DiffLst1->SelectedItem == 0)
				Diff1 = GV.ImageMatrix;
			else if (DiffLst1->SelectedItem == 1)
				Diff1 = GV.NoiseMatrix;
			else
				Diff1 = GV.FiltrMatrix;

			if (DiffLst2->SelectedItem == 0)
				Diff2 = GV.ImageMatrix;
			else if (DiffLst2->SelectedItem == 1)
				Diff2 = GV.NoiseMatrix;
			else
				Diff2 = GV.FiltrMatrix;

			for (DWORD i = 0; i < GV.ImageSize.cx * GV.ImageSize.cy; i++)
			{
				GV.DiffrMatrix[i]._1 = 128 + (Diff1[i]._1 >> 1) - (Diff2[i]._1 >> 1);
				GV.DiffrMatrix[i]._2 = 128 + (Diff1[i]._2 >> 1) - (Diff2[i]._2 >> 1);
				GV.DiffrMatrix[i]._3 = 128 + (Diff1[i]._3 >> 1) - (Diff2[i]._3 >> 1);
				GV.DiffrMatrix[i].a = 0xff;
			}
			// GetPSNR
			double PSNR = GetPSNR(GV.DiffrMatrix, GV.ImageSize.cx, GV.ImageSize.cy);
			WCHAR String[16];
			DWORD Len;
			if (PSNR < -.5 || PSNR > 99)
			{
				String[0] = L'#'; String[1] = L'Б'; String[2] = L'е';
				String[3] = L'c'; String[4] = L'к'; String[5] = L'.';
				Len = 6;
			}
			else
				GetValueString(String, 0, PSNR, 2, &Len);
			String[Len] = L'д'; String[Len + 1] =L'Б'; String[Len + 2] = 0;
			((PictureText *) GetControlByID(35))->SetText(String, 0);
			// Transform to rgb
			SetColors(GV.DiffImg, (DWORD *) GV.DiffrMatrix);
			((PictureDraggableImage *) GetControlByID(15))->InsertSurface(GV.DiffImg);
		}
		GV.LoadingPhase = 0;
		GV.LockInterface = timeGetTime();
		SetCursor(LoadCursor(0, IDC_ARROW));
	}
	// Разблокировка интерфейса после отработки всех команд,
	// поступивших во время работы программы
	if (GV.LockInterface > 1)
		if (timeGetTime() - GV.LockInterface > 100)
		{
			GV.LockInterface = 0;
		}
	LPD3DXSPRITE drawer;
	D3DXCreateSprite(GV.pD3Device, &drawer);
	GV.pD3Device->BeginScene();
	drawer->Begin(D3DXSPRITE_ALPHABLEND);
	PictureControl *ToRender = GV.MyControls;
	while (ToRender)
	{
		if (ToRender->Flags & ELEMENT_SHOWN)
			ToRender->Redraw(drawer);
		ToRender = ToRender->Next;
	}
	if (GV.OnTopImage.Image)
	{
		drawer->Draw(GV.OnTopImage.Image,&GV.OnTopImage.Coords, 0, &GV.OnTopPlace, GV.OnTopColor);
	}
	drawer->End();
	GV.pD3Device->EndScene();
	drawer->Release();
	GV.pD3Device->Present(0,0,0,0);
	return S_OK;
}

/********************************************************************
* Collecting pixels from the surface 
* and forming the DWORD matrix 
* Matrix length has to be h * w of image
********************************************************************/

HRESULT GetColors (LPDIRECT3DSURFACE9 surf, DWORD* Matrix)
{
	D3DLOCKED_RECT r;
	D3DSURFACE_DESC desc; 
	surf->GetDesc(&desc);
	if(surf->LockRect(&r, 0, D3DLOCK_READONLY) == S_OK)
	{
		DWORD n = 0;
		DWORD *dst = (DWORD*) r.pBits;
		for (int i = 0; i < desc.Height; i++)
		{
			for (int j = 0; j < desc.Width; j++)
			{
				Matrix[n] = *(dst + j);
				n++;
			}
			dst += r.Pitch / 4;
		}
		surf->UnlockRect();
	}
	return S_OK;
}

/********************************************************************
* Collecting pixels from the Macroblock matrix 
* and forming the surface
********************************************************************/

HRESULT SetColors (LPDIRECT3DSURFACE9 surf, DWORD* Matrix)
{
	D3DLOCKED_RECT r;
	D3DSURFACE_DESC desc; 
	surf->GetDesc(&desc);
	if(surf->LockRect(&r, 0, 0) == S_OK)
	{
		int n = 0;
		int *dst = (int*) r.pBits;
		for (int i = 0; i < desc.Height; i++)
		{
			for (int j = 0; j < desc.Width; j++)
			{
				*(dst + j) = Matrix[n];
				n++;
			}
			dst += r.Pitch / 4;
		}
		surf->UnlockRect();
	}
	return S_OK;
}